#include "stdafx.h"

#include "core/image.h"
#include "rt/basic_definitions.h"
#include "rt/geometry_group.h"

#include "impl/lwobject.h"
#include "impl/phong_shaders.h"
#include "impl/basic_primitives.h"
#include "impl/perspective_camera.h"
#include "impl/integrator.h"
#include "rt/renderer.h"
#include "impl/samplers.h"


class BumpMirrorPhongShader: public DefaultPhongShader {
protected:
	float2 m_texCoord;
	Point m_position;
	Vector m_tang, m_biNorm;
public:
	float reflCoef;

	virtual void setPosition(const Point& _point) {
		m_position = _point;
	}

	//Set the tangent to (0, 1, 0)
	virtual void setNormal(const Vector& _normal) {
		DefaultPhongShader::setNormal(_normal);
		m_tang = Vector(0, -1, 0);
		m_biNorm = ~_normal % m_tang;
	}

	virtual Vector getNormal() const {
		Vector ret = m_normal;

		float d;
		float2 t1 = float2(modf(m_texCoord.x, &d), modf(m_texCoord.y, &d))
				- float2(0.5f, 0.5f);

		float dist = t1.x * t1.x + t1.y * t1.y;
		const float R = 0.25;
		const float DIV = 10.f;
		if (dist < R * R) {
			Vector newNorm(t1.x / DIV, t1.y / DIV, sqrtf(R * R - dist / (DIV
					* DIV)));
			ret = newNorm.x * m_tang + newNorm.y * m_biNorm + newNorm.z
					* m_normal;
			ret = ~ret;
		}

		return ret;
	}

	virtual float4 getIndirectRadiance(const Vector &_out,
			Integrator *_integrator) const {
		Ray r;
		r.o = m_position;

		Vector n = getNormal();
		Vector v = n * fabs(n * _out);
		r.d = _out + 2 * (v - _out);

		return _integrator->getRadiance(r) * float4::rep(reflCoef);
	}

	virtual void setTextureCoord(const float2& _texCoord) {
		m_texCoord = _texCoord;
	}

	_IMPLEMENT_CLONE(BumpMirrorPhongShader)
	;

};

inline void runOrigin(){
Image img(800, 600);
	img.addRef();

	//Set up the scene
	GeometryGroup scene;
	LWObject cow;
	cow.read("models/cow.obj", true);
	cow.addReferencesToScene(scene.primitives);
	scene.rebuildIndex();




	BumpMirrorPhongShader sh4;
	sh4.diffuseCoef = float4(0.2f, 0.2f, 0, 0);
	sh4.ambientCoef = sh4.diffuseCoef;
	sh4.specularCoef = float4::rep(0.8f);
	sh4.specularExponent = 10000.f;
	sh4.reflCoef = 0.4f;
	sh4.addRef();
	cow.materials[cow.materialMap["Floor"]].shader = &sh4;

	//Enable bi-linear filtering on the walls
	((TexturedPhongShader*) cow.materials[cow.materialMap["Stones"]].shader.data())->diffTexture->filterMode
			= Texture::TFM_Bilinear;
	((TexturedPhongShader*) cow.materials[cow.materialMap["Stones"]].shader.data())->amibientTexture->filterMode
			= Texture::TFM_Bilinear;

	CheckBoard3DShader sh;
	sh.scale = float4::rep(4);
	sh.addRef();


	MirrorPhongShader sh5;
	sh5.diffuseCoef = float4(0.f, 0.f, 0.f, 0);
	sh5.ambientCoef = sh5.diffuseCoef;
	sh5.specularCoef = float4::rep(0.f);
	sh5.specularExponent = 10000.f;
	sh5.reflCoef = 0.8f;
	sh5.addRef();

	DefaultGlassShader sh6;
	sh6.diffuseCoef = float4(0.f, 0.f, 0.f, 0);
	sh6.ambientCoef = sh6.diffuseCoef;
	sh6.specularCoef = float4::rep(0.1f);
	sh6.specularExponent = 10000.f;
	//sh6.reflCoef = 0.4f;
	sh6.dielectric_para = .8f;
	sh6.n0 = 1.f;
	sh6.nt = 0.3f;
	sh6.color_filter = float4::rep(1.f);
	//sh6.color_filter = float4(0.2f,0.2f, 0.9f, 0.f);
	sh6.addRef();

	//cow.materials[cow.materialMap["Cow_White"]].shader = &sh6;
	//cow.materials[cow.materialMap["Cow_Purple"]].shader = &sh6;
	//cow.materials[cow.materialMap["Floor"]].shader = &sh;
	//cow.materials[cow.materialMap["Stones"]].shader = &sh;
	//Set up the cameras
	/*
	PerspectiveCamera cam1(Point(-9.398149f, -6.266083f, 5.348377f), Point(
			-6.324413f, -2.961229f, 4.203216f), Vector(0, 0, 1), 30,
			std::make_pair(img.width(), img.height()));
	*/
	PerspectiveCamera cam2(Point(2.699700f, 6.437226f, 0.878297f), Point(
			4.337114f, 8.457443f, -0.019007f), Vector(0, 0, 1), 30,
			std::make_pair(img.width(), img.height()));
	/*
	DofCamera cam1(Point(2.f, -4.f, 3.f), Point(0.f, 0.f, 3.f), Vector(0, 0, 1), 30.f, img.width(),
			img.height(), 0.001f);
	*/
	ThinLensCamera cam1(Point(2.f, -3.f, 3.f), Point(1.f, 0.f, 3.f), Vector(0, 0, 1),
		30.f,std::make_pair(img.width(), img.height()));
	cam1.fp_distance = 1.f;
	//cam1.lens_radius = 0.02f;
	cam1.focal_stop = 200;

	cam1.addRef();
	cam2.addRef();

	//Set up the integrator
	IntegratorImpl integrator;
	integrator.addRef();
	integrator.scene = &scene;
	PointLightSource pls;

	pls.falloff = float4(0, 0, 1, 0);

	pls.intensity = float4::rep(0.9f);
	pls.position = Point(2.473637f, -3.119330f, 5.571486f);
	integrator.lightSources.push_back(pls);

	integrator.ambientLight = float4::rep(0.1f);

	//DefaultSampler samp;
	StratifiedSampler samp;
	samp.addRef();
	samp.samplesX = 5;
	samp.samplesY = 5;

	//Render
	Renderer r;
	r.integrator = &integrator;
	r.target = &img;
	r.sampler = &samp;

	r.camera = &cam1;
	r.render();
	img.writePNG("result_cam1.png");

	/*
	//For seeing the difference in texture filtering
	r.camera = &cam2;
	r.render();
	img.writePNG("result_cam2.png");
	*/
}

inline void runNew(){

	Image img(800, 600);
	img.addRef();

	//Set up the scene
	GeometryGroup scene;
	LWObject cow;
	std::string model_obj = "mycowscene07";
	std::string model_dir = "models/";
	std::string sceneobj = model_dir.append(model_obj);
	sceneobj.append(".obj");
	std::cout<<sceneobj<<std::endl;
	cow.read(sceneobj, true);
	//cow.read("models/chess_suzanne_Scene.obj", true);
	cow.addReferencesToScene(scene.primitives);
	scene.rebuildIndex();

	BumpMirrorPhongShader sh4;
	sh4.diffuseCoef = float4(0.2f, 0.2f, 0, 0);
	sh4.ambientCoef = sh4.diffuseCoef;
	sh4.specularCoef = float4::rep(0.8f);
	sh4.specularExponent = 10000.f;
	sh4.reflCoef = 0.4f;
	sh4.addRef();
	//cow.materials[cow.materialMap["Floor"]].shader = &sh4;


	MirrorPhongShader sh5;
	sh5.diffuseCoef = float4(0.4f, 0.4f, 0, 0);
	sh5.ambientCoef = sh4.diffuseCoef;
	sh5.specularCoef = float4::rep(0.8f);
	sh5.specularExponent = 10000.f;
	sh5.reflCoef = 0.4f;
	sh5.addRef();

	DefaultPhongShader dfs;
	dfs.ambientCoef = float4(.7f, 0, 0, 0); dfs.diffuseCoef = dfs.ambientCoef;
	dfs.specularExponent = 1000.f; dfs.specularCoef = float4::rep(.8f);

	//cow.materials[cow.materialMap["Material.001"]].shader = &sh5;
	//cow.materials[cow.materialMap["Material.003"]].shader = &sh5;
	//cow.materials[cow.materialMap["Cow_White"]].shader = &sh5;

	CheckBoard3DShader sh;
	sh.scale = float4::rep(4);
	sh.addRef();


	//cow.materials[cow.materialMap["Material.002"]].shader = &sh;
	//cow.materials[cow.materialMap["Material.006"]].shader = &sh;
	//cow.materials[cow.materialMap["Material.005"]].shader = &sh;
	cow.materials[cow.materialMap["Cow_White"]].shader = &dfs;
	cow.materials[cow.materialMap["Cow_Purple"]].shader = &dfs;
	//cow.materials[cow.materialMap["Material.003"]].shader = &sh5;

	std::cout<<sceneobj<<std::endl;

 	//Enable bi-linear filtering on the walls
	//((TexturedPhongShader*)cow.materials[cow.materialMap["Stones"]].shader.data())->diffTexture->filterMode = Texture::TFM_Point;
	//((TexturedPhongShader*)cow.materials[cow.materialMap["Stones"]].shader.data())->amibientTexture->filterMode = Texture::TFM_Point;
	//((TexturedPhongShader*)cow.materials[cow.materialMap["Stones"]].shader.data())->diffTexture->filterMode = Texture::TFM_Bilinear;
 	//((TexturedPhongShader*)cow.materials[cow.materialMap["Stones"]].shader.data())->amibientTexture->filterMode = Texture::TFM_Bilinear;


	//Set up the cameras

	//PerspectiveCamera cam1(Point(-9.398149f, -6.266083f, 5.348377f), Point(-6.324413f, -2.961229f, 4.203216f), Vector(0, 0, 1), 30,
		//std::make_pair(img.width(), img.height()));

	PerspectiveCamera cam2(Point(2.699700f, 6.437226f, 0.878297f), Point(4.337114f, 8.457443f,- 0.019007f), Vector(0, 0, -1), 30,
		std::make_pair(img.width(), img.height()));

	PerspectiveCamera cam1(Point(4.f, -4.f, 4.f), Point(0, 0, 1.5f), Vector(0, 0, 1), 30,
		std::make_pair(img.width(), img.height()));
	//DofCamera cam1(Point(3.f, -7.f, 4.f), Point(0, 0, 0), Vector(0, 0, 1), 30.f, img.width(), img.height(), 0.05f);
	//DofCamera cam1(Point(-7.898149f, -4.266083f, 4.348377f), Point(-6.324413f, -2.961229f, 4.203216f), Vector(0, 0, 1),
		//30.f, img.width(), img.height(), 0.005f);
	//DofCamera cam1(Point(0.38f, -8.8f, -0.31f), Point(-0.74f, -2.04f, -1.13f), Vector(0, 0, 1),
		//30.f, img.width(), img.height(), 0.0075f);
	cam1.addRef();
	cam2.addRef();

	//Set up the integrator
	IntegratorImpl integrator;
	integrator.addRef();
	integrator.scene = &scene;
	PointLightSource pls;

	pls.falloff = float4(0, 0, 1, 0);

	pls.intensity  = float4::rep(0.9f);
	//pls.intensity  = float4::rep(0.9f);
	pls.position = Point(10, -10.f, 15.f);;
	//pls.position = Point(-2.473637f, 3.119330f, 9.571486f);
	integrator.lightSources.push_back(pls);

	integrator.ambientLight = float4::rep(0.1f);

	DefaultSampler samp;
	//StratifiedSampler samp;
	//samp.m = 4;
	samp.addRef();

	//Render
	Renderer r;
	r.integrator = &integrator;
	r.target = &img;
	r.sampler = &samp;

	r.camera = &cam1;
	r.render();
	std::string output = model_obj.append(".png");
	img.writePNG(output);

	//For seeing the difference in texture filtering
	//r.camera = &cam2;
	//r.render();
	//img.writePNG("result_cam2.png");
}



void assigment4_1_and_2() {
	runOrigin();
	//runNew();
}

